﻿using System;
using System.Text;

namespace CommServer.Common.Json
{
    internal sealed class JsonReader
    {
        private readonly StringBuilder _builder = new StringBuilder();
        private readonly char[] _json;

        /// <summary>
        ///     解析时，是否有异常发生
        /// </summary>
        private bool _exception;

        private int _index;

        /// <summary>
        ///     是否不抛出异常，返回转换是否成功
        /// </summary>
        private bool _isReturn;

        internal JsonReader(string jsonString)
        {
            _json = jsonString.ToCharArray();
        }

        private JsonValue ParseByToken(Token token)
        {
            if (_exception)
            {
                return null;
            }
            switch (token)
            {
                case Token.Number:
                    return ParseNumber();

                case Token.String:
                    return ParseString();

                case Token.BeginObject:
                    return ParseObject();

                case Token.BeginArray:
                    return ParseArray();

                case Token.True:
                    return true;

                case Token.False:
                    return false;

                case Token.Null:
                    return null;

                default:
                    if (_isReturn)
                    {
                        _exception = true;
                        return null;
                    }
                    throw new Exception("unrecognized token at index" + _index);
            }
        }

        public JsonValue Parser()
        {
            return ParseValue();
        }

        public bool TryParser(out JsonValue value)
        {
            _isReturn = true;
            value = ParseValue();
            return !_exception;
        }

        /// <summary>
        ///     解析值（可能为对象、数组、字符串等JSON支持的类型）
        /// </summary>
        /// <returns></returns>
        private JsonValue ParseValue()
        {
            return ParseByToken(NextToken());
        }

        /// <summary>
        ///     解析对象
        /// </summary>
        /// <returns></returns>
        private JsonValue ParseObject()
        {
            var obj = new JsonObject();
            while (true)
            {
                if (_exception)
                {
                    return null;
                }

                switch (NextToken())
                {
                    case Token.Comma: //逗号分隔符，跳过后继续添加
                        continue;
                    case Token.EndObject:
                        return obj;
                    default:
                        string name = ParseString();
                        if (NextToken() != Token.Colon)
                        {
                            ExceptionHandle("expected colon at index " + _index);
                            return null;
                        }
                        JsonValue value = ParseValue();
                        obj[name] = value;
                        break;
                }
            }
        }

        /// <summary>
        ///     解析数组
        /// </summary>
        /// <returns></returns>
        private JsonValue ParseArray()
        {
            var array = new JsonArray();
            while (true)
            {
                if (_exception)
                {
                    return null;
                }
                Token nextToken = NextToken();
                switch (nextToken)
                {
                    case Token.Comma:
                        continue;
                    case Token.EndArray:
                        return array;
                    default:
                        array.Add(ParseByToken(nextToken));
                        break;
                }
            }
        }

        /// <summary>
        ///     解析字符串
        /// </summary>
        /// <returns></returns>
        private JsonValue ParseString()
        {
            _builder.Clear();
            while (_index < _json.Length)
            {
                char c = _json[_index++];

                if (c == '"') //字符串结束标志
                {
                    return _builder.ToString();
                }
                if (c == '\\') //处理转义字符
                {
                    switch (_json[_index++])
                    {
                        case '"':
                            _builder.Append('"');
                            break;

                        case '\\':
                            _builder.Append('\\');
                            break;

                        case '/':
                            _builder.Append('/');
                            break;

                        case 'b':
                            _builder.Append('\b');
                            break;

                        case 'f':
                            _builder.Append('\f');
                            break;

                        case 'n':
                            _builder.Append('\n');
                            break;

                        case 'r':
                            _builder.Append('\r');
                            break;

                        case 't':
                            _builder.Append('\t');
                            break;

                        case 'u':
                            {
                                int remainingLength = _json.Length - _index;
                                if (remainingLength < 4)
                                {
                                    break;
                                }

                                // parse the 32 bit hex into an integer codepoint
                                uint codePoint = ParseUnicode(_json, _index);
                                _builder.Append((char)codePoint);

                                // skip 4 chars
                                _index += 4;
                            }
                            break;
                    }
                }
                else
                {
                    _builder.Append(c);
                }
            }
            ExceptionHandle("unexpectedly reached end of string");
            return null;
        }

        /// <summary>
        ///     解析数值
        /// </summary>
        /// <returns></returns>
        private JsonValue ParseNumber()
        {
            int start = _index - 1;
            bool dec = false;

            #region 确定数值值字符的结束位置

            while (_index < _json.Length)
            {
                char ch = _json[_index];
                if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-' || ch == '+' || ch == 'e' || ch == 'E')
                {
                    _index++;
                    if (ch == '.' || ch == 'e' || ch == 'E')
                    {
                        //设置小数标志位
                        dec = true;
                    }
                }
                else
                {
                    break;
                }
            }

            #endregion

            if (dec) //小数
            {
                double result;
                if (double.TryParse(new String(_json, start, _index - start), out result))
                {
                    return result;
                }
                ExceptionHandle("double parse error");
                return result;
            }
            else //整数
            {
                long result;
                if (long.TryParse(new String(_json, start, _index - start), out result))
                {
                    return result;
                }
                ExceptionHandle("int parse error");
                return result;
            }
        }

        private void ExceptionHandle(string message)
        {
            if (_isReturn)
            {
                _exception = true;
            }
            else
            {
                throw new Exception(message);
            }
        }

        private uint ParseChar(char c, uint multipliyer)
        {
            uint p = 0;
            if (c >= '0' && c <= '9')
            {
                p = (uint)(c - '0') * multipliyer;
            }
            else if (c >= 'A' && c <= 'F')
            {
                p = (uint)((c - 'A') + 10) * multipliyer;
            }
            else if (c >= 'a' && c <= 'f')
            {
                p = (uint)((c - 'a') + 10) * multipliyer;
            }
            return p;
        }

        private uint ParseUnicode(char[] chars, int i)
        {
            uint p1 = ParseChar(chars[i], 0x1000);
            uint p2 = ParseChar(chars[i + 1], 0x100);
            uint p3 = ParseChar(chars[i + 2], 0x10);
            uint p4 = ParseChar(chars[i + 3], 1);
            return p1 + p2 + p3 + p4;
        }

        private Token NextToken()
        {
            while (_index < _json.Length)
            {
                if (_json[_index] > ' ')
                {
                    break;
                }
                _index++;
            }
            if (_index == _json.Length)
            {
                ExceptionHandle("reached end of string unexpectedly");
                return Token.None;
            }

            char c = _json[_index++];
            switch (c)
            {
                case '{':
                    return Token.BeginObject;
                case '}':
                    return Token.EndObject;
                case '[':
                    return Token.BeginArray;
                case ']':
                    return Token.EndArray;
                case ',':
                    return Token.Comma;
                case '"':
                    return Token.String;
                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                case '-':
                case '+':
                case '.':
                    return Token.Number;
                case ':':
                    return Token.Colon;
                case 'f':

                    #region 推断 false

                    if (_json.Length - _index >= 4 &&
                        _json[_index + 0] == 'a' &&
                        _json[_index + 1] == 'l' &&
                        _json[_index + 2] == 's' &&
                        _json[_index + 3] == 'e')
                    {
                        _index += 4;
                        return Token.False;
                    }

                    #endregion

                    break;
                case 't':

                    #region 推断 true

                    if (_json.Length - _index >= 3 &&
                        _json[_index + 0] == 'r' &&
                        _json[_index + 1] == 'u' &&
                        _json[_index + 2] == 'e')
                    {
                        _index += 3;
                        return Token.True;
                    }

                    #endregion

                    break;
                case 'n':

                    #region 推断 null

                    if (_json.Length - _index >= 3 &&
                        _json[_index + 0] == 'u' &&
                        _json[_index + 1] == 'l' &&
                        _json[_index + 2] == 'l')
                    {
                        _index += 3;
                        return Token.Null;
                    }

                    #endregion

                    break;
            }
            ExceptionHandle("could not find token at index " + (_index - 1));
            return Token.None;
        }

        private enum Token
        {
            None,
            BeginObject,
            EndObject,
            BeginArray,
            EndArray,
            Colon,
            Comma,
            String,
            Number,
            True,
            False,
            Null
        }
    }
}
